<?php
/**
 * Trait TProxy.
 * 
 * @package Framework
 * @subpackage Core
 * @author Agent Cooper <twinpeaks.agent.cooper@gmail.com>
 */
namespace TwinPeaks\Core;

trait TProxy
{
	/**
	 * @var object
	 */
	private $proxyDelegate;
	
	/**
	 * @return void
	 */
	private function proxyErrNoDelegate()
	{
		throw new \Exception(sprintf('No delegate class was assigned for the class %s',
			get_called_class()));
	}
	
	/**
	 * @return void
	 */
	private function proxyErrDelegateAssigned()
	{
		throw new \Exeption(sprintf('Proxy delegate for the class %s has been assigned',
			get_called_class()));
	}
	
	/**
	 * @return void
	 */
	private function proxyErrInaccessibleProperty($name)
	{
		throw new \Exception(sprintf('Trying to access inaccessible property %s->%s',
			get_called_class(), $name));
	}
	
	/**
	 * Sets proxy delegate.
	 * 
	 * @param object $delegate Object to dispatch method calls.
	 * @final
	 * @access protected
	 * @return void
	 */
	final protected function setProxyDelegate($delegate)
	{
		// You can't assign proxy delegate more than one time. If you try to do it
		// then probably you are doing something wrong.
		if (isset($this->proxyDelegate))
			$this->proxyErrDelegateAssigned();
		
		$this->proxyDelegate($delegate);
	}

	/**
	 * Returns proxy delegate.
	 * 
	 * @final
	 * @access public
	 * @return object
	 */
	final function getProxyDelegate()
	{
		if (!isset($this->proxyDelegate))
			$this->proxyErrNoDelegate();
		
		return $this->proxyDelegate;
	}
	
	/**
	 * @return mixed
	 */
	final function __call($method, $args)
	{
		// Before dispatching method call to a proxy delegate we have to check
		// whether it was assigned.
		if (!isset($this->proxyDelegate))
			$this->proxyErrNoDelegate();
		
		if (method_exists($this->proxyDelegate, $method))
			return call_user_func_array ([$this->proxyDelegate, $method], $args);
	}
	
	/**
	 * Gets value of a proxy delegate property.
	 * 
	 * @final
	 * @access public
	 * @return void
	 */
	final function __get($name)
	{
		if (!isset($this->proxyDelegate))
			$this->proxyErrNoDelegate();
		
		// Check the access to object property.
		if (!isset($this->proxyDelegate->{$name}))
			$this->proxyErrInaccessibleProperty($name);
		
		return $this->proxyDelegate->{$name};
	}
	
	/**
	 * Sets value to a proxy delegate property.
	 * 
	 * @final
	 * @access public
	 * @return void
	 */
	final function __set($name, $value)
	{
		if (!isset($this->proxyDelegate))
			$this->proxyErrNoDelegate();
		
		// Check the access to object property
		if (!isset($this->proxyDelegate->{$name}))
			$this->proxyErrInaccessibleProperty($name);
		
		return $this->proxyDelegate->{$name} = $value;
	}
}
